package logger

import (
	"bytes"
	"fmt"
	"goblog/internal/settings"
	"os"
	"path"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
)

var (
	Logger *logrus.Logger
	once   sync.Once
)

// 颜色
const (
	red    = 31
	yellow = 33
	blue   = 36
	gray   = 37
)

// LogFormatter 实现 logrus.Formatter，支持彩色输出
type LogFormatter struct{}

// FileDateHook 实现 logrus.Hook，日志按日期写入文件
type FileDateHook struct {
	file     *os.File
	logPath  string
	fileDate string //判断日期切换目录
	appName  string
}

// Format 方法自定义日志格式
func (t *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	//根据不同的level去展示颜色
	var levelColor int
	switch entry.Level {
	case logrus.DebugLevel, logrus.TraceLevel:
		levelColor = gray
	case logrus.WarnLevel:
		levelColor = yellow
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		levelColor = red
	default:
		levelColor = blue
	}

	var b *bytes.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &bytes.Buffer{}
	}

	//自定义日期格式
	timestamp := entry.Time.Format("2006-01-02 15:04:05")

	if entry.HasCaller() { // 获取调用函数和文件名
		//自定义文件路径
		funcVal := entry.Caller.Function
		fileVal := fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line)

		// 彩色日志格式
		fmt.Fprintf(b, "[%s] \x1b[%dm[%s]\x1b[0m %s %s %s\n", timestamp, levelColor, entry.Level, fileVal, funcVal, entry.Message)
	} else {
		fmt.Fprintf(b, "[%s] \x1b[%dm[%s]\x1b[0m %s\n", timestamp, levelColor, entry.Level, entry.Message)
	}
	return b.Bytes(), nil
}

// Levels 方法：适用于所有日志级别
func (hook *FileDateHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

// Fire 方法：写入日志文件
func (hook *FileDateHook) Fire(entry *logrus.Entry) error {
	timer := entry.Time.Format("2006-01-02")
	line, _ := entry.String()

	if hook.fileDate == timer { // 日期未变，直接写入
		hook.file.Write([]byte(line))
		return nil
	}

	// 日期变了，关闭旧文件，创建新文件
	hook.file.Close()
	os.MkdirAll(fmt.Sprintf("%s/%s", hook.logPath, timer), os.ModePerm)
	filename := fmt.Sprintf("%s/%s/%s.log", hook.logPath, timer, hook.appName)

	hook.file, _ = os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	hook.fileDate = timer
	hook.file.Write([]byte(line))
	return nil
}

// InitFile 方法：初始化日志文件，并绑定 Hook
func InitFile(logPath, appName string) (*FileDateHook, error) {
	fileDate := time.Now().Format("2006-01-02")

	//创建目录
	err := os.MkdirAll(fmt.Sprintf("%s/%s", logPath, fileDate), os.ModePerm)
	if err != nil {
		return nil, err
	}

	filename := fmt.Sprintf("%s/%s/%s.log", logPath, fileDate, appName)
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	if err != nil {
		return nil, err
	}

	hook := &FileDateHook{file, logPath, fileDate, appName}
	return hook, nil
}

// InitLogrus 方法：初始化 Logger
func InitLogrus() {
	once.Do(func() { // 使用 sync.Once 确保 InitLogrus() 只执行一次
		Logger = logrus.New()

		Logger.SetReportCaller(true)         //开启返回函数名和行号
		Logger.SetFormatter(&LogFormatter{}) //设置自己定义的Formatter

		// 从配置读取日志级别
		cfgLog := settings.Cfg.Log
		level, err := logrus.ParseLevel(cfgLog.Level)
		if err != nil {
			level = logrus.InfoLevel
		}
		Logger.SetLevel(level)

		// 初始化日志文件 Hook
		hook, err := InitFile(cfgLog.Dir, cfgLog.App)
		if err != nil {
			fmt.Println("日志文件初始化失败:", err)
			return
		}

		// 设置日志输出到终端和文件
		Logger.SetOutput(os.Stdout) //设置输出类型
		Logger.AddHook(hook)        //添加hook
	})
}
